package com.spring.boot.core.utils;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

import com.google.common.collect.Lists;

public class ClassUtils {

	public static List<Class<?>> getClassList(String pkgName, boolean isRecursive, Class<?> parent) {
		List<Class<?>> classList = Lists.newArrayList();
		ClassLoader loader = Thread.currentThread().getContextClassLoader();
		try {
			// 按文件的形式去查找
			String strFile = pkgName.replaceAll("\\.", "/");
			Enumeration<URL> urls = loader.getResources(strFile);

			while (urls.hasMoreElements()) {
				URL url = urls.nextElement();
				if (url != null) {
					String protocol = url.getProtocol();
					String pkgPath = url.getPath();
					if ("file".equals(protocol)) {
						System.out.println("file:" + pkgPath);
						findClassName(classList, pkgName, pkgPath, isRecursive, parent);
					} else if ("jar".equals(protocol)) {
						findClassName(classList, pkgName, url, isRecursive, parent);
					}
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}

		return classList;
	}

	public static void findClassName(List<Class<?>> clazzList, String pkgName, String pkgPath, boolean isRecursive, Class<?> parent) {
		if (clazzList == null) {
			return;
		}
		File[] files = filterClassFiles(pkgPath);// 过滤出.class文件及文件夹
		if (files != null) {
			for (File f : files) {
				String fileName = f.getName();
				if (f.isFile()) {
					// .class 文件的情况
					String clazzName = getClassName(pkgName, fileName);
					addClassName(clazzList, clazzName, parent);
				} else {
					// 文件夹的情况
					if (isRecursive) {
						// 需要继续查找该文件夹/包名下的类
						String subPkgName = pkgName + "." + fileName;
						String subPkgPath = pkgPath + "/" + fileName;
						findClassName(clazzList, subPkgName, subPkgPath, true, parent);
					}
				}
			}
		}
	}

	/**
	 * 第三方Jar类库的引用。<br/>
	 * 
	 * @throws IOException
	 */
	public static void findClassName(List<Class<?>> clazzList, String pkgName, URL url, boolean isRecursive, Class<?> parent) throws IOException {
		JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
		JarFile jarFile = jarURLConnection.getJarFile();
		System.out.println("jarFile:" + jarFile.getName());
		Enumeration<JarEntry> jarEntries = jarFile.entries();
		while (jarEntries.hasMoreElements()) {

			JarEntry jarEntry = jarEntries.nextElement();
			String jarEntryName = jarEntry.getName(); // 类似：sun/security/internal/interfaces/TlsMasterSecret.class
			String clazzName = jarEntryName.replace("/", ".");

			int endIndex = clazzName.lastIndexOf(".");
			String prefix = null;
			if (endIndex > 0) {
				String prefix_name = clazzName.substring(0, endIndex);
				endIndex = prefix_name.lastIndexOf(".");
				if (endIndex > 0) {
					prefix = prefix_name.substring(0, endIndex);
				}
			}
			if (prefix != null && clazzName.endsWith(".class")) {
				if (prefix.equals(pkgName)) {
					addClassName(clazzList, clazzName, parent);
				} else if (isRecursive && prefix.startsWith(pkgName)) {
					// 遍历子包名：子类
					addClassName(clazzList, clazzName, parent);
				}
			}
		}
	}

	private static File[] filterClassFiles(String pkgPath) {
		if (pkgPath == null) {
			return null;
		}
		// 接收 .class 文件 或 类文件夹
		return new File(pkgPath).listFiles(new FileFilter() {
			@Override
			public boolean accept(File file) {
				return (file.isFile() && file.getName().endsWith(".class")) || file.isDirectory();
			}
		});
	}

	private static String getClassName(String pkgName, String fileName) {
		int endIndex = fileName.lastIndexOf(".");
		String clazz = null;
		if (endIndex >= 0) {
			clazz = fileName.substring(0, endIndex);
		}
		String clazzName = null;
		if (clazz != null) {
			clazzName = pkgName + "." + clazz;
		}
		return clazzName;
	}

	private static void addClassName(List<Class<?>> clazzList, String clazzName, Class<?> parent) {
		if (clazzList != null && clazzName != null) {
			if (clazzName.endsWith(".class"))
				clazzName = clazzName.substring(0, clazzName.lastIndexOf("."));
			Class<?> clazz = null;
			try {
				clazz = Class.forName(clazzName);
			} catch (ClassNotFoundException e) {
				e.printStackTrace();
			}
			if (clazz != null) {
				if (parent == null) {
					clazzList.add(clazz);
				} else if (parent.isAssignableFrom(clazz) && parent != clazz) {
					clazzList.add(clazz);
				}
			}
		}
	}
}